// stl_forward /C++11万能引用,完美转发
// C++11 标准为 C++ 引入右值引用语法的同时，还解决了一个 C++ 98/03
// 标准长期存在的短板， 即使用简单的方式即可在函数模板中实现参数的完美转发。
//那么，什么是完美转发？它为什么是 C++98/03 标准存在的一个短板？C++11
//标准又是如何为 C++ 弥补这一短板的？
//完美转发指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。
//所谓完美，即不仅能准确地转发参数的值，还能保证被转发参数的左、右值属性不变。
//在 C++
//中，一个表达式不是左值就是右值。有关如何判断一个表达式是左值还是右值，可阅读《C++右值引用》一文做详细了解。
//如上所示，function() 函数模板中调用了 otherdef()
//函数。在此基础上，完美转发指的是： 如果 function() 函数接收到的参数 t
//为左值，那么该函数传递给 otherdef() 的参数 t 也是左值； 反之如果 function()
//函数接收到的参数 t 为右值，那么传递给 otherdef() 函数的参数 t 也必须为右值。
//显然，function() 函数模板并没有实现完美转发。一方面，参数 t
//为非引用类型，这意味着在调用 function() 函数时，
//实参将值传递给形参的过程就需要额外进行一次拷贝操作；另一方面，无论调用
// function() 函数模板时传递给参数 t 的是左值还是右值， 对于函数内部的参数 t
//来说，它有自己的名称，也可以获取它的存储地址，因此它永远都是左值，
//也就是说，传递给 otherdef() 函数的参数 t
//永远都是左值。总之，无论从那个角度看，function() 函数的定义都不“完美”。
//读者可能会问，完美转发这样严苛的参数传递机制，很常用吗？C++98/03
//标准中几乎不会用到， 但 C++11 标准为 C++
//引入了右值引用和移动语义，因此很多场景中是否实现完美转发，
//直接决定了该参数的传递过程使用的是拷贝语义（调用拷贝构造函数）还是移动语义（调用移动构造函数）。
//事实上，C++98/03 标准下的 C++
//也可以实现完美转发，只是实现方式比较笨拙。通过前面的学习我们知道， C++ 98/03
// 标准中只有左值引用，并且可以细分为非 const 引用和 const 引用。其中，使用非
// const 引用作为函数模板参数时， 只能接收左值，无法接收右值；而 const
//左值引用既可以接收左值，也可以接收右值， 但考虑到其 const
//属性，除非被调用函数的参数也是 const 属性，否则将无法直接传递。
//这也就意味着，单独使用任何一种引用形式，可以实现转发，但无法保证完美。
//因此如果使用 C++ 98/03 标准下的 C++
//语言，我们可以采用函数模板重载的方式实现完美转发，例如

#include <iostream>
using namespace std;

void otherdef(int& t) { printf("lvalue=%d\n", t); }
void otherdef(const int& t) { printf("rvalue=%d\n", t); }

// == 普通，非引用类型，需要额外进行一次拷贝操作，永远左值
template <typename T>
void function_n(T t) {
  otherdef(t);
}

// == 重载函数模板，分别接收左值和右值
// 仅适用于模板函数仅有少量参数的情况
template <typename T>
void function_lrv(const T& t) {
  otherdef(t);
}
template <typename T>
void function_lv(T& t) {
  otherdef(t);
}

// == 万能引用,完美转发,引用折叠
template <typename T>
void function_forward(T&& t) {
  otherdef(forward<T>(t));
}

// ==== test
int testFunc() {
  int x = 1;
  function_n(5);        // 左右
  function_n(x);        // 左右
  function_lrv(5);      // 左右
  function_lv(x);       // 限左
  function_forward(5);  // rvalue
  function_forward(x);  // lvalue
  return 0;
}

// 面试题目
struct A {
  A(int&& n) { std::cout << "r," << n << endl; }
  A(int& n) { std::cout << "l," << n << endl; }
};

class Bac {
 public:
  template <class T1, class T2, class T3>
  Bac(T1&& t1, T2&& t2, T3&& t3)
      : a1_(std::forward<T1>(t1)), a2_(std::forward<T2>(t2)), a3_(t3) {}

 private:
  A a1_, a2_, a3_;
};

int testMove() {
  int j = 2;
  Bac b(std::move(j), j, std::move(j));  // rll
  return 0;
}
